﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net;

namespace sdd.subsystems.ui
{
    // basic menu item - has a name but no value; used for root- and top-level menus
    public class MenuItem
    {
        public MenuItem Parent { set; get; }
        public string Name { set; get; }
        public string Caption { set; get; }
        public List<MenuItem> SubItems { set; get; }
        public bool Selected { get; set; }

        public MenuItem(string itemName, string itemCaption)
        {
            SubItems = new List<MenuItem>();
            Name = itemName;
            Caption = itemCaption;
        }
    }

    // link menu item - navigates to the linked screen (IScreen implementation)
    public class LinkMenuItem : MenuItem
    {
        public IScreen LinkedScreen { set; get; }
        public LinkMenuItem(string itemName, string itemCaption, IScreen linkedScreen) : base(itemName, itemCaption)
        {
            LinkedScreen = linkedScreen;
        }
    }

    // clickable menu items can be "clicked" with the center button and raise a click event
    public interface IClickableMenuItem
    {
        void Click();
    }

    // menu item can be increased/decreased
    public interface ICountableMenuItem
    {
        void Increase(bool largeStep = false);
        void Decrease(bool largeStep = false);
    }

    // menu item can be toggled between states
    public interface IToggleableMenutItem
    {
        void Toggle();
    }

    // menu item can be browsed (next/previous)
    public interface IBrowseableMenuItem
    {
        void Next();
        void Previous();
    }

    // menu item can be printed (on LCD/UI)
    public interface IPrintableValueMenuItem
    {
        event EventHandler<MenuItemFormattingEventArgs> Formatting;
        string Print();
    }


    // event args for item click
    public class MenuItemClickEventArgs<T> : EventArgs
    {
        public MenuItemClickEventArgs(T clickedValue)
        {
            ClickedValue = clickedValue;
        }
        public T ClickedValue { get; private set; }
    }

    public class MenuItemFormattingEventArgs : EventArgs
    {
        public MenuItemFormattingEventArgs(object itemValue, string formattedValue)
        {
            Value = itemValue;
            FormattedValue = formattedValue;
        }

        public object Value { get; private set; }
        public string FormattedValue { get; set; }
    }

    // observable menu items provide notifications whenever the item value changes
    public abstract class ObservableMenuItem<T> : MenuItem
    {
        protected ObservableMenuItem(string itemName, string itemCaption) : base(itemName, itemCaption) { }
        public abstract event EventHandler<ObservableMenuItemValueChangedEventArgs<T>> ValueChanged;
    }

    // event args for observable menu item value change (generic type)
    public class ObservableMenuItemValueChangedEventArgs<T> : EventArgs
    {
        public ObservableMenuItemValueChangedEventArgs(T oldValue, T newValue)
        {
            OldValue = oldValue;
            NewValue = newValue;
        }
        public T OldValue { get; private set; }
        public T NewValue { get; private set; }
    }

    // boolean menu item - can be either enabled (true) or disabled (false)
    public class SwitchMenuItem : ObservableMenuItem<bool>, IToggleableMenutItem, IPrintableValueMenuItem
    {
        private bool _Value;

        public override event EventHandler<ObservableMenuItemValueChangedEventArgs<bool>> ValueChanged;

        public event EventHandler<MenuItemFormattingEventArgs> Formatting;

        public SwitchMenuItem(string itemName, string itemCaption, bool initValue) : base(itemName, itemCaption)
        {
            Value = initValue;
        }

        public bool Value
        {
            get
            {
                return _Value;
            }
            set
            {
                bool oldValue = _Value;
                _Value = value;
                if (ValueChanged != null) { ValueChanged(this, new ObservableMenuItemValueChangedEventArgs<bool>(oldValue, _Value)); }
            }
        }

        public void Toggle()
        {
            Value = !Value;
        }

        public string Print()
        {
            string formattedValue = _Value ? "Enabled" : "Disabled";

            MenuItemFormattingEventArgs e = new MenuItemFormattingEventArgs(_Value, formattedValue);
            if (Formatting != null) { Formatting(this, e); }
            return e.FormattedValue;
        }
    }

    // integer menu item - can be set to any integer value between min and max
    public class IntMenuItem : ObservableMenuItem<int>, ICountableMenuItem, IPrintableValueMenuItem
    {
        private int _Value;
        private object calcLock = new Object();

        public override event EventHandler<ObservableMenuItemValueChangedEventArgs<int>> ValueChanged;

        public event EventHandler<MenuItemFormattingEventArgs> Formatting;

        public int Min { set; get; }
        public int Max { set; get; }
        public int Step { set; get; }
        public int StepLarge { set; get; }
        public string Unit { set; get; }

        public int Value
        {
            get { return _Value; }
            set
            {
                if (value > Max | value < Min) { throw new ArgumentOutOfRangeException(); }
                int oldValue = _Value;
                _Value = value;
                if (ValueChanged != null) { ValueChanged(this, new ObservableMenuItemValueChangedEventArgs<int>(oldValue, _Value)); }
            }
        }

        public IntMenuItem(string itemName, string itemCaption, int initValue, int minValue, int maxValue, int step = 1, int stepLarge = 10) : base(itemName, itemCaption)
        {
            Min = minValue;
            Max = maxValue;
            Step = step;
            StepLarge = stepLarge;
            Value = initValue;
        }

        public void Increase(bool stepLarge = false)
        {
            lock (calcLock)
            {
                if (stepLarge) { Value = Value + StepLarge; } else { Value = Value + Step; }
            }
        }

        public void Decrease(bool stepLarge = false)
        {
            lock (calcLock)
            {
                if (stepLarge) { Value = Value - StepLarge; } else { Value = Value - Step; }
            }
        }

        public string Print()
        {
            string formattedValue = _Value.ToString() + Unit;

            MenuItemFormattingEventArgs e = new MenuItemFormattingEventArgs(_Value, formattedValue);
            if (Formatting != null) { Formatting(this, e); }
            return e.FormattedValue;

        }
    }

    // IP address menu item - holds an IP address
    public class IPAddressMenuItem : ObservableMenuItem<IPAddress>, IPrintableValueMenuItem
    {
        private IPAddress _Value;

        public override event EventHandler<ObservableMenuItemValueChangedEventArgs<IPAddress>> ValueChanged;

        public event EventHandler<MenuItemFormattingEventArgs> Formatting;

        public IPAddressMenuItem(string itemName, string itemCaption, IPAddress initValue) : base(itemName, itemCaption)
        {
            Value = initValue;
        }

        public IPAddress Value
        {
            get { return _Value; }
            set
            {
                IPAddress oldValue = _Value;
                _Value = value;
                if (ValueChanged != null) { ValueChanged(this, new ObservableMenuItemValueChangedEventArgs<IPAddress>(oldValue, _Value)); }
            }
        }

        public string Print()
        {
            string formattedValue = _Value.ToString();

            MenuItemFormattingEventArgs e = new MenuItemFormattingEventArgs(_Value, formattedValue);
            if (Formatting != null) { Formatting(this, e); }
            return e.FormattedValue;

        }
    }

    // The OptionsMenuItem holds a list of possible selections w/ name/value (similar to a combo box in winforms).
    public class OptionsMenuItem<T> : ObservableMenuItem<T>, IClickableMenuItem, IBrowseableMenuItem, IPrintableValueMenuItem
    {
        private List<OptionsMenuItem<T>.ListEntry<T>> _SelectList;
        private int _SelectedListEntry = 0;

        public override event EventHandler<ObservableMenuItemValueChangedEventArgs<T>> ValueChanged;
        public event EventHandler<MenuItemClickEventArgs<T>> ItemClick;
        public event EventHandler<MenuItemFormattingEventArgs> Formatting;


        public OptionsMenuItem(string itemName, string itemCaption, List<OptionsMenuItem<T>.ListEntry<T>> selectionList, int initialSelectedEntryIndex) : base(itemName, itemCaption)
        {
            _SelectList = selectionList;
            _SelectedListEntry = initialSelectedEntryIndex;
        }

        public int ActiveEntryIndex;

        public int SelectedEntryIndex
        {
            get
            {
                return _SelectedListEntry;
            }
        }


        public string SelectedEntryName
        {
            get
            {
                return _SelectList.ElementAt(_SelectedListEntry - 1).Name;
            }
        }

        public T SelectedEntryValue
        {
            get
            {
                return _SelectList.ElementAt(_SelectedListEntry - 1).Value;
            }
        }


        public void Next()
        {
            int nextElement = _SelectedListEntry + 1;
            if (nextElement > _SelectList.Count) { nextElement = 1; }
            _SelectedListEntry = nextElement;
        }

        public void Previous()
        {
            int nextElement = _SelectedListEntry - 1;
            if (nextElement < 1) { nextElement = _SelectList.Count; }
            _SelectedListEntry = nextElement;
        }

        public void Click()
        {
            if (ItemClick != null) { ItemClick(this, new MenuItemClickEventArgs<T>(SelectedEntryValue)); }
        }




        public string Print()
        {
            string formattedValue = "";
            if (SelectedEntryIndex == ActiveEntryIndex)
            {
                formattedValue = "[ " + SelectedEntryName + " ]"; 
            }
            else
            {
                formattedValue = SelectedEntryName;
            }
            
            MenuItemFormattingEventArgs e = new MenuItemFormattingEventArgs(SelectedEntryName, formattedValue);
            if (Formatting != null) { Formatting(this, e); }
            return e.FormattedValue;

        }

        public class ListEntry<T>
        {
            public ListEntry(string itemName, T itemValue )
            {
                Name = itemName;
                Value = itemValue;
            }
            public string Name { get; set; }
            public T Value { get; set; }
        }

    }

    // Generic menu item holds any value
    public class GenericMenuItem<T> : ObservableMenuItem<T>, IPrintableValueMenuItem
    {
        private T _Value;

        public override event EventHandler<ObservableMenuItemValueChangedEventArgs<T>> ValueChanged;

        public event EventHandler<MenuItemFormattingEventArgs> Formatting;

        public GenericMenuItem(string itemName, string itemCaption, T initValue) : base(itemName, itemCaption)
        {
            Value = initValue;
        }

        public T Value
        {
            get { return _Value; }
            set
            {
                T oldValue = _Value;
                _Value = value;
                if (ValueChanged != null) { ValueChanged(this, new ObservableMenuItemValueChangedEventArgs<T>(oldValue, _Value)); }
            }
        }

        public string Print()
        {
            string formattedValue = _Value.ToString();
            MenuItemFormattingEventArgs e = new MenuItemFormattingEventArgs(_Value, formattedValue);
            if (Formatting != null) { Formatting(this, e); }
            return e.FormattedValue;
        }
    }

}
